﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Eneta.Portal.Common.Dto;

using Microsoft.SharePoint;

namespace Eneta.Portal.Common.Repository
{
    /// <summary>
    /// Repository for user profile operations.
    /// </summary>
    public class UserProfileRepository : BaseRepository
    {        
        
        public UserProfileRepository(SPWeb web) : base(web)
        {
            
        }

        public IList<UserProfileDto> GetLastJoinedUsers(int limit)
        {
            var queryString = "<Where><And><Or>";
            queryString += "<Eq><FieldRef Name='Locked' /><Value Type='Boolean'>0</Value></Eq>";
            queryString += "<IsNull><FieldRef Name='Locked' /></IsNull></Or>";
            queryString += "<IsNotNull><FieldRef Name='Title' /></IsNotNull>";
            queryString += "</And></Where>";
            queryString += "<OrderBy><FieldRef Name='Created' Ascending='False' /></OrderBy>";

            var users = QueryProfiles(queryString, limit);
            return users;
        }

        /// <summary>
        /// Gets the user profile by id.
        /// </summary>
        /// <param name="profileId">The profile id.</param>
        public virtual UserProfileDto GetUserProfileById(int profileId)
        {
            var dto = GetFromCache(profileId);
            if (dto != null)
            {
                //Logger.LogInfo("GetUserProfileById: from cache " + profileId, string.Empty);
                return dto;
            }
            var queryString = "<Where><Eq><FieldRef Name='ID' /><Value Type='Integer'>{0}</Value></Eq></Where>";
            queryString = string.Format(queryString, profileId);

            var profiles = QueryProfiles(queryString, 1);
            if (profiles == null)
                return null;
            if (profiles.Count == 0)
                return null;

            var profile = profiles[0];

            //Logger.LogInfo("GetUserProfileById: NO CACHE " + profileId, string.Empty);
            AddToCache(profile);

            return profile;
        }
        
        /// <summary>
        /// Gets the user profile by user name.
        /// </summary>
        /// <param name="userName">User name.</param>
        public virtual UserProfileDto GetUserProfileByUserName(string userName)
        {
            return GetUserProfileByUserName(userName, false);
        }

        /// <summary>
        /// Gets the user profile by user name.
        /// </summary>
        /// <param name="userName">User name.</param>
        /// <param name="refreshCache">if set to <c>true</c> refreshes profile item in cache.</param>
        public virtual UserProfileDto GetUserProfileByUserName(string userName, bool refreshCache)
        {
            if(string.IsNullOrEmpty(userName))
            {
                //Logger.LogWarning("GetUserProfileByUserName(): Username is null or empty", GetType().ToString());
                return null;
            }                        

            if (userName.IndexOf(':') >= 0)
                userName = userName.Substring(userName.IndexOf(':') + 1);

            UserProfileDto profile = GetFromCacheByUserName(userName);
            if (profile != null)
            {
                Logger.LogInfo("GetUserProfileByUserName: from cache " + userName, string.Empty);
                return profile;
            }
            var queryString = "<Where><Eq><FieldRef Name='SysID' /><Value Type='Text'>{0}</Value></Eq></Where>";
            queryString = string.Format(queryString, userName);

            var profiles = QueryProfiles(queryString, 1);
            if (profiles.Count == 0)
                return null;

            profile = profiles[0];
            AddToCache(profile);

            //Logger.LogInfo("GetUserProfileUserName: NO CACHE " + userName, string.Empty);
            return profile;
        }

        public virtual UserProfileDto GetUserProfileByProfileName(string profileName)
        {
            if (string.IsNullOrEmpty(profileName))
            {
                return null;
            }

            UserProfileDto profile;

            var queryString = "<Where><Eq><FieldRef Name='Title' /><Value Type='Text'>{0}</Value></Eq></Where>";
            queryString = string.Format(queryString, profileName);

            var profiles = QueryProfiles(queryString, 0);
            if (profiles.Count == 0)
                return null;

            profile = profiles[0];
            return profile;
        }
        // / <summary>
        // / Gets the user profile fillment percent.
        // / </summary>
        // / <param name="profileItem">The profile list item.</param>
        public virtual int GetUserProfileFillmentPercent(SPListItem profileItem)
        {
            if (profileItem == null)
                throw new ArgumentNullException("profileItem");

            var indicatorValue = 0;

            if (IndicatorFieldHasValue(profileItem, "FirstName"))
                indicatorValue += 5;
            if (IndicatorFieldHasValue(profileItem, "LastName"))
                indicatorValue += 5;
            if (IndicatorFieldHasValue(profileItem, "MyInterest"))
                indicatorValue += 5;
            if (IndicatorFieldHasValue(profileItem, "EMail"))
                indicatorValue += 5;

            if (IndicatorFieldHasValue(profileItem, "AboutMe"))
                indicatorValue += 20;
            if (IndicatorFieldHasValue(profileItem, "Blog") || IndicatorFieldHasValue(profileItem, "WorkAddress"))
                indicatorValue += 20;
            if (profileItem.Attachments != null)
                if (profileItem.Attachments.Count > 0)
                    indicatorValue += 20;
            if (IndicatorFieldHasValue(profileItem, "School") || IndicatorFieldHasValue(profileItem, "Company"))
                indicatorValue += 20;

            return indicatorValue;
        }
        
        private static bool IndicatorFieldHasValue(SPListItem item, string fieldName)
        {
            if (item == null)
                return false;
            if (string.IsNullOrEmpty(fieldName))
                return false;

            var fieldId = item.Fields.GetFieldByInternalName(fieldName).Id;
            var valueObject = item[fieldId];
            if (valueObject == null)
                return false;
            var valueString = valueObject.ToString();
            return !string.IsNullOrEmpty(valueString);
        }

        /// <summary>
        /// Saves the specified profile.
        /// </summary>
        /// <param name="profile">The profile to save.</param>
        public virtual void Save(UserProfileDto profile)
        {
            
        }

        public DateTime GetLastChangeDate()
        {
            var modified = DateTime.MinValue;

            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using (var site = new SPSite(SiteId))
                using (var web = site.OpenWeb())
                {
                    var list = web.Lists["ProfileList"];
                    var queryString = "<OrderBy><FieldRef Name='Modified' Ascending='False' /></OrderBy>";
                    var query = new SPQuery { Query = queryString, RowLimit = 1 };
                    query.ViewFields = "<FieldRef Name='Modified' />";
                    var items = list.GetItems(query);

                    if (items == null)
                        return;
                    if (items.Count == 0)
                        return;

                    modified = (DateTime)items[0]["Modified"];
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return modified;
        }

        private IList<UserProfileDto> QueryProfiles(string queryString, int limit)
        {
            var profiles = new List<UserProfileDto>();

            var sec = new SPSecurity.CodeToRunElevated(
                delegate
                    {
                        using (var site = new SPSite(SiteId))
                        using (var web = site.OpenWeb())
                        {
                            var list = web.Lists["ProfileList"];
                            
                            var items = ExecuteQuery(list, queryString, limit);
                            if (items == null)
                                return;

                            foreach (SPListItem item in items)
                                profiles.Add(CreateProfileDto(item));
                        }
                    });
            SPSecurity.RunWithElevatedPrivileges(sec);
            return profiles;
        }

        private UserProfileDto CreateProfileDto(SPListItem item)
        {
            var dto = new UserProfileDto();

            dto.Id = item.ID;
            dto.UserName = item.Title;
            dto.Title = item.Title;
            dto.LiveIdToken = (string) item["SysID"];
            dto.Created = (DateTime)item["Created"];
            dto.Modified = (DateTime) item["Modified"];

            var fieldId = item.Fields.GetFieldByInternalName("MyInterest").Id;
            if(item[fieldId] != null)
            {
                var interestsValue = new SPFieldMultiChoiceValue(item[fieldId].ToString());
                for (var i = 0; i < interestsValue.Count; i++)
                    dto.Interests.Add(interestsValue[i]);
            }

            fieldId = item.Fields.GetFieldByInternalName("FirstName").Id;
            if (item[fieldId] != null)
                dto.FirstName = item[fieldId].ToString();

            fieldId = item.Fields.GetFieldByInternalName("ShowFirstName").Id;
            if (item[fieldId] != null)
                dto.ShowFirstName = (bool)item[fieldId];

            fieldId = item.Fields.GetFieldByInternalName("LastName").Id;
            if (item[fieldId] != null)
                dto.LastName = item[fieldId].ToString();

            fieldId = item.Fields.GetFieldByInternalName("ShowLastName").Id;
            if (item[fieldId] != null)
                dto.ShowLastName = (bool)item[fieldId];

            fieldId = item.Fields.GetFieldByInternalName("EMail").Id;
            if (item[fieldId] != null)
                dto.Email = item[fieldId].ToString();

            fieldId = item.Fields.GetFieldByInternalName("ShowEmail").Id;
            if (item[fieldId] != null)
                dto.ShowEmail = (bool)item[fieldId];


            fieldId = item.Fields.GetFieldByInternalName("AboutMe").Id;
            if (item[fieldId] != null)
                dto.AboutMe = item[fieldId].ToString();

            fieldId = item.Fields.GetFieldByInternalName("Blog").Id;
            if (item[fieldId] != null)
            {
                var blogLink = new SPFieldUrlValue(item[fieldId].ToString());
                dto.Blog = blogLink.Url;
            }
            fieldId = item.Fields.GetFieldByInternalName("ShowAddress").Id;
            if (item[fieldId] != null)
                dto.ShowAddress = (bool) item[fieldId];

            fieldId = item.Fields.GetFieldByInternalName("WorkAddress").Id;
            if (item[fieldId] != null)
                dto.Address = item[fieldId].ToString();

            fieldId = item.Fields.GetFieldByInternalName("ShowPhone").Id;
            if (item[fieldId] != null)
                dto.ShowPhone = (bool)item[fieldId];

            fieldId = item.Fields.GetFieldByInternalName("Phone").Id;
            if (item[fieldId] != null)
                dto.Phone = item[fieldId].ToString();

            fieldId = item.Fields.GetFieldByInternalName("ShowSchool").Id;
            if (item[fieldId] != null)
                dto.ShowSchool = (bool)item[fieldId];

            fieldId = item.Fields.GetFieldByInternalName("School").Id;
            if (item[fieldId] != null)
                dto.School = item[fieldId].ToString();

            fieldId = item.Fields.GetFieldByInternalName("ShowFieldOfStudy").Id;
            if (item[fieldId] != null)
                dto.ShowFieldOfStudy = (bool)item[fieldId];

            fieldId = item.Fields.GetFieldByInternalName("Speciality").Id;
            if (item[fieldId] != null)
                dto.FieldOfStudy = item[fieldId].ToString();

            fieldId = item.Fields.GetFieldByInternalName("ShowCompany").Id;
            if (item[fieldId] != null)
                dto.ShowCompany = (bool)item[fieldId];

            fieldId = item.Fields.GetFieldByInternalName("Company").Id;
            if (item[fieldId] != null)
                dto.Company = item[fieldId].ToString();

            fieldId = item.Fields.GetFieldByInternalName("ShowJobTitle").Id;
            if (item[fieldId] != null)
                dto.ShowJobTitle = (bool)item[fieldId];

            fieldId = item.Fields.GetFieldByInternalName("JobTitle").Id;
            if (item[fieldId] != null)
                dto.JobTitle = item[fieldId].ToString();

            fieldId = item.Fields.GetFieldByInternalName("ProfileFilledDate").Id;
            if (item[fieldId] != null)
                dto.ProfileCompletelyFilledDate = (DateTime)item[fieldId];

            if (item["Locked"] != null)
                dto.Locked = (bool) item["Locked"];

            /*
             * Let's ask images from sitecollectionimages
            if (item.Attachments != null)
                if (item.Attachments.Count > 0)
                    foreach (string fileName in item.Attachments)
                        if (fileName.Contains("32x32"))
                            dto.UserImage32x32Url = item.Attachments.UrlPrefix + fileName;
                        else if (fileName.Contains("48x48"))
                            dto.UserImage48x48Url = item.Attachments.UrlPrefix + fileName;
                        else if (fileName.Contains("64x64"))
                            dto.UserImage64x64Url = item.Attachments.UrlPrefix + fileName;
            */
            var sizes = new[] {32, 48, 64};

            foreach(var size in sizes)
            {
                var imageUrl = "/SiteCollectionImages/users/" + item.ID + "_" + size + "x" + size + ".png";
                var file = item.Web.GetFile(imageUrl);

                if (file == null || !file.Exists)
                    if (size == 64)
                    {
                        imageUrl = "/Style Library/eneta/images/user_64x64.jpg";
                    }
                    else 
                        imageUrl = "/Style Library/eneta/images/user.gif";

                if (size == 32)
                    dto.UserImage32x32Url = imageUrl;
                else if (size == 48)
                    dto.UserImage48x48Url = imageUrl;
                else if (size == 64)
                    dto.UserImage64x64Url = imageUrl;
            }

            return dto;
        }

        private UserProfileDto GetFromCache(int userId)
        {
            var cache = HttpContext.Current.Cache;
            if (cache["Profiles"] == null)
                return null;

            var profiles = cache["Profiles"] as IList<UserProfileDto>;
            if (profiles == null)
                return null;

            var query = from p in profiles
                        where p.Id == userId
                        select p;

            var profile = query.FirstOrDefault();
            //if(profile != null)
            //    Logger.LogInfo("user found from cache: " + userId, string.Empty);

            return profile;
        }

        private UserProfileDto GetFromCacheByUserName(string userName)
        {
            var cache = HttpContext.Current.Cache;
            if (cache["Profiles"] == null)
                return null;

            var profiles = cache["Profiles"] as IList<UserProfileDto>;
            if (profiles == null)
                return null;

            var query = from p in profiles
                        where p.LiveIdToken == userName
                        select p;

            var profile = query.FirstOrDefault();
            return profile;
        }

        private void AddToCache(UserProfileDto dto)
        {
            var cache = HttpContext.Current.Cache;
            
            var profiles = cache["Profiles"] as IList<UserProfileDto>;
            if (profiles == null)
                profiles = new List<UserProfileDto>();

            if (!profiles.Contains(dto))
                profiles.Add(dto);

            cache["Profiles"] = profiles;
        }

        public void RemoveFromCache(int userId)
        {
            var cache = HttpContext.Current.Cache;
            
            var profiles = cache["Profiles"] as IList<UserProfileDto>;
            if (profiles == null)
                return;
            
            var profileQuery = from p in profiles
                                where p.Id == userId
                                select p;
            var profile = profileQuery.FirstOrDefault();

            if(profiles.Contains(profile))
                profiles.Remove(profile);
        }
    }
}